package com.liachen.stock.ext.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.chimmhuang.excel.ExcelHelper;
import com.github.chimmhuang.excel.tablemodel.ExcelWorkbook;
import com.github.chimmhuang.excel.tablemodel.Row;
import com.github.chimmhuang.excel.tablemodel.SheetTable;
import com.google.common.collect.Lists;
import com.liachen.common.constant.Constant;
import com.liachen.common.util.DateUtil;
import com.liachen.common.util.FileUtils;
import com.liachen.common.util.ToolsUtil;
import com.liachen.stock.entity.etf.Etf;
import com.liachen.stock.entity.etf.EtfBrief;
import com.liachen.stock.entity.etf.EtfReportData;
import com.liachen.stock.entity.etf.EtfRoe;
import com.liachen.stock.entity.etf.EtfValuation;
import com.liachen.stock.entity.etf.LixingerBaseReq;
import com.liachen.stock.ext.LixingerService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 理杏仁服务
 *
 * @author d85v8h9 chen.liang
 * @date 2021/12/24 10:45
 * @since V1.0
 */
@Service
@Slf4j
public class LixingerServiceImpl implements LixingerService {
    private static final String GET_ETF_LIST_URL = "https://www.lixinger.com/api/fund/funds/etfs";
    /**
     * 获取财务数据，如营业收入、ROE等。
     * https://www.lixinger.com/open/api/doc?api-key=a%2Findex%2Ffs
     */
    private static final String GET_FINANCE_URL = "https://open.lixinger.com/api/%s/index/fs";
    /**
     * 获取基本面数据，如PE、PB等。
     * https://www.lixinger.com/open/api/doc?api-key=a%2Findex%2Ffundamental
     */
    private static final String GET_VALUATION_URL = "https://open.lixinger.com/api/%s/index/fundamental";

    private static final String ADD_FOLLOW_URL = "https://www.lixinger.com/api/ugd/stock/followed/";
    @Override
    public List<Etf> getEtfList() throws IOException {
        List<Etf> etfList = this.getEtfListByMarket("a");
        etfList.addAll(this.getEtfListByMarket("h"));
        etfList.addAll(this.getEtfListByMarket("us"));
        return etfList;
    }

    @Override
    public List<Etf> getEtfListByMarket(String market) throws IOException {
        FormBody formBody = new FormBody.Builder()
                .add("market", market)
                .build();
        Request request = new Request.Builder()
                .url(GET_ETF_LIST_URL)
                .header("cookie", Constant.LIXINGER_COOKIE)
                .method("POST", formBody)
                .build();

        Response response = ToolsUtil.getClient().newCall(request).execute();
        String responseStr = response.body().string();
        return JSONArray.parseArray(responseStr, Etf.class);
    }

    @Override
    public List<EtfRoe> getRoeInfo(List<String> stockCodeList) throws IOException {
        List<EtfRoe> etfRoeList = this.getRoeInfoByMarket(stockCodeList, "a");
//        etfRoeList.addAll(this.getRoeInfoByMarket(stockCodeList, "h"));
//        etfRoeList.addAll(this.getRoeInfoByMarket(stockCodeList, "us"));
        return etfRoeList;
    }

    @Override
    public List<EtfRoe> getRoeInfoByMarket(List<String> stockCodeList, String market) throws IOException {
        if(stockCodeList.size() > 100) {
            stockCodeList = stockCodeList.subList(0, 99);
        }
        LixingerBaseReq baseReq = LixingerBaseReq.builder()
                .date("2020-12-31")
                .stockCodes(stockCodeList)
                .metricsList(Lists.newArrayList("y.m.roe.t"))
                .build();

        JSONObject jsonObject = this.postJsonRequest(baseReq, String.format(GET_FINANCE_URL, market));
        List<EtfRoe> etfRoeList = Lists.newArrayList();
        JSONArray data = jsonObject.getJSONArray("data");
        if(CollectionUtil.isEmpty(data)) {
            return etfRoeList;
        }
        for(int i=0; i<data.size(); i++) {
            JSONObject object = data.getJSONObject(i);
            EtfRoe etfRoe = EtfRoe.builder()
                    .stockCode(object.getString("stockCode"))
                    .roe(Optional.ofNullable(object.getJSONObject("y")).map(y -> y.getJSONObject("m")).map(m -> m.getJSONObject("roe")).map(roe -> roe.getDoubleValue("t")*100).orElse(0D))
                    .build();
            etfRoeList.add(etfRoe);
        }
        return etfRoeList;
    }

    @Override
    public List<EtfValuation> getEtfValuation(List<String> stockCodeList) throws IOException {
        List<EtfValuation> etfValuationList = this.getEtfValuationByMarket(stockCodeList, "cn");
        etfValuationList.addAll(this.getEtfValuationByMarket(stockCodeList, "hk"));
        etfValuationList.addAll(this.getEtfValuationByMarket(stockCodeList, "us"));
        return etfValuationList;
    }

    @Override
    public List<EtfValuation> getEtfValuationByMarket(List<String> stockCodeList, String market) throws IOException {
        String date = DateUtil.getCurrDate(DateUtil.LONG_DATE_FORMAT);
        if(StringUtils.equalsIgnoreCase("us", market)) {
            date = DateUtil.getBeforeWorkDay(DateUtil.LONG_DATE_FORMAT);
        }
        LixingerBaseReq baseReq = LixingerBaseReq.builder()
                .date(date)
//                .date("2021-12-31")
                .stockCodes(stockCodeList)
                .metricsList(Lists.newArrayList("pe_ttm.y10.mcw",
                        "pe_ttm.mcw",
                        "pb.y10.mcw",
                        "pb.mcw",
                        "ps_ttm.y10.mcw",
                        "ps_ttm.mcw"))
                .build();
        JSONObject jsonObject = this.postJsonRequest(baseReq, String.format(GET_VALUATION_URL, market));
        JSONArray data = jsonObject.getJSONArray("data");
        List<EtfValuation> etfValuationList = Lists.newArrayListWithCapacity(data.size());
        for(int i=0; i<data.size(); i++) {
            JSONObject object = data.getJSONObject(i);
            EtfValuation etfValuation = EtfValuation.builder()
                    .stockCode(object.getString("stockCode"))
                    .pe(Optional.ofNullable(object.getJSONObject("pe_ttm")).map(y -> y.getDoubleValue("mcw")).orElse(-1D))
                    .pePos(Optional.ofNullable(object.getJSONObject("pe_ttm")).map(y -> y.getJSONObject("y10")).map(y10 -> y10.getJSONObject("mcw")).map(mcw -> mcw.getDoubleValue("cvpos") * 100).orElse(-100D))
                    .pb(Optional.ofNullable(object.getJSONObject("pb")).map(y -> y.getDoubleValue("mcw")).orElse(-1D))
                    .pbPos(Optional.ofNullable(object.getJSONObject("pb")).map(y -> y.getJSONObject("y10")).map(y10 -> y10.getJSONObject("mcw")).map(mcw -> mcw.getDoubleValue("cvpos") * 100).orElse(-100D))
                    .ps(Optional.ofNullable(object.getJSONObject("ps_ttm")).map(y -> y.getDoubleValue("mcw")).orElse(-1D))
                    .psPos(Optional.ofNullable(object.getJSONObject("ps_ttm")).map(y -> y.getJSONObject("y10")).map(y10 -> y10.getJSONObject("mcw")).map(mcw -> mcw.getDoubleValue("cvpos") * 100).orElse(-100D))
                    .build();
            etfValuationList.add(etfValuation);
        }
        return etfValuationList;
    }

    @Override
    public void addFollow(String groupId, String stockId) {
        FormBody formBody = new FormBody.Builder()
                .add("stockId", stockId)
                .build();
        Request request = new Request.Builder()
                .url(ADD_FOLLOW_URL + groupId)
                .header("cookie", "jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWM1MjJmZGNiZGUxZjM5MDBiNGRlMzUiLCJpYXQiOjE2NDAzMDk1MDEsImV4cCI6MTY0MTUxOTEwMX0.ciyMQ_-DkQ8q1n2bs2d0WTQKRZEhKLXJANxEz5n5hss; lang=cmn_hans_cn")
                .method("POST", formBody)
                .build();
        try {
            Response response = ToolsUtil.getClient().newCall(request).execute();
            JSONObject jsonObject = JSONObject.parseObject(response.body().string());
            if(Objects.isNull(jsonObject) || !Objects.equals("OK", jsonObject.getString("message"))) {
                System.out.println("添加失败stockId：" + stockId);
            }
        } catch (IOException e) {
            System.out.println("添加失败stockId：" + stockId);
        }
    }

    private static final String ETF_ORG_FILE_NAME = "ETF分析模板.xlsx";

    @Override
    public void exportEtfBrief(List<EtfBrief> etfBriefList) {
        try {
            // 获取 excel 二进制文件
            File file = new File("src/test/resource/templates/demo_etf.xlsx");
            byte[] bytes = FileUtils.readFileToByteArray(file);

            // 创建 table 对象
            ExcelWorkbook excelWorkbook = ExcelHelper.createWorkbook(bytes);
            SheetTable table = excelWorkbook.getSheet(0);

            // 设置 自定义 sheet 页名称
            table.setSheetName("估值表");

            EtfReportData tableData = new EtfReportData();

            /*
            ====================================
                       开始组装表格数据
            ====================================
            */
            tableData.setTitle(DateUtil.currDay());

            tableData.setEtfBriefList(etfBriefList);

            /*
            ====================================
                       表格数据组装完毕
                       -------------
                       开始动态设置表格
            ====================================
            */

            Row veryLow = table.getRow(7);
            Row low = table.getRow(8);
            Row normal = table.getRow(9);
            Row high = table.getRow(10);
            // 将 rowNum 大于 7 的都删除，进行动态表格添加
            table.removeRowGE(7);
            int rowStartNum = 7;
            AtomicInteger rowEndNum = new AtomicInteger(rowStartNum);
            DecimalFormat decimalFormat = new DecimalFormat("0.0%");
            tableData.getEtfBriefList().stream().sorted(Comparator.comparing(EtfBrief::getAvgPos)).forEach(etfBrief -> {
                // 复制模板行
                Row copy = null;
                if(etfBrief.isTooLow()) {
                    copy = veryLow;
                } else if (etfBrief.isLow()) {
                    copy = low;
                } else if (etfBrief.isNormal()) {
                    copy = normal;
                } else {
                    copy = high;
                }
                copy.getCell("B").setValue(etfBrief.getName() + "\r\n" + etfBrief.getStockCode());
                copy.getCell("C").setValue(String.format("%.1f", etfBrief.getAvgPos()));
                copy.getCell("D").setValue(decimalFormat.format(etfBrief.getRoe()));
                copy.getCell("E").setValue(decimalFormat.format(etfBrief.getOneYearClosePointChange()));
                table.appendRow(copy);
                rowEndNum.getAndIncrement();
            });

            // 设置指定行的单元格样式，【此处仅展示功能：加粗，rowNum 建议动态计算】
//            table.getRow(rowEndNum.get() - 1).setBorderStyle(BorderPositionEnum.BOTTOM, BorderStyle.MEDIUM);

            // 将变量的值填充进表格
            ExcelHelper.fillInData(table, tableData);


            // 获取转换后的二进制（支持多 sheet 导出）
            byte[] bytes1 = ExcelHelper.convert2Byte(table, excelWorkbook.getSheet(1));

            // 获取桌面路径
//            FileSystemView fsv = FileSystemView.getFileSystemView();
//            String path = fsv.getHomeDirectory().getPath();
            String path = "src/test/resource/etf";
            String filePath = String.format("%s/%s.xlsx", path, DateUtil.currDay());

            File targetFile = new File(filePath);
            FileOutputStream fos = new FileOutputStream(targetFile);
            fos.write(bytes1);

            fos.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

//    @Override
//    public void exportEtfBrief(List<EtfBrief> etfBriefList) {
//        try {
//            FileInputStream file =
//                    new FileInputStream(new File(String.format("%s\\%s", Constant.FILE_PATH, ETF_ORG_FILE_NAME)));
//
//            XSSFWorkbook workbook = new XSSFWorkbook(file);
//            XSSFSheet sheet = workbook.getSheetAt(0);
//            String beforelevel = null;
//            etfBriefList.stream().sorted(Comparator.comparing(EtfBrief::getAvgPos)).forEach(etfBrief -> {
//                int rowNum = 1;
//                XSSFRow row = sheet.getRow(rowNum);
//                if(etfBrief.isTooLow()) {
//                    XSSFCellStyle cellStyle = row.getRowStyle();
//                    cellStyle.setFillBackgroundColor(IndexedColors.OLIVE_GREEN.getIndex());
//                    cellStyle.setAlignment(HorizontalAlignment.CENTER);
//                } else if (etfBrief.isLow()) {
//
//                } else if (etfBrief.isNormal()) {
//
//                } else {
//
//                }
//                rowNum++;
//            });
//
//            sheet.setForceFormulaRecalculation(true);
//            file.close();
//            FileOutputStream outFile =
//                    new FileOutputStream(new File(String.format("%s\\%s.xlsx", path, DateUtil.currDay())));
//            workbook.write(outFile);
//            outFile.close();
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//            log.error("导出数据失败，stockCode = {}", etfBrief.getStockCode());
//        }
//    }

    private JSONObject postJsonRequest(LixingerBaseReq baseReq, String url) throws IOException {
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, JSONObject.toJSONString(baseReq));
        Request request = new Request.Builder()
                .url(url)
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .build();
        Response response = ToolsUtil.getClient().newCall(request).execute();
        return JSONObject.parseObject(response.body().string());
    }
}
